package de.mmis.core.pojop.answer;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.apache.log4j.Logger;

import de.mmis.core.base.Toolbox;
import de.mmis.core.base.abstracttree.InnerNode;
import de.mmis.core.base.abstracttree.LeafNode;
import de.mmis.core.base.abstracttree.Tree;
import de.mmis.core.sexpression.SExpressionWriter;

public class AbstractAnswer implements Answer {

	private final static Logger LOGGER = Logger.getLogger(AbstractAnswer.class);

	public enum Type {
		SExpression, Bytes, Error, OK, Quit, Event,
	}

	private final Type type;

	public Type getType() {
		return this.type;
	}

	// used by SExpression type
	private Tree expression;

	// used by Bytes type
	private byte[] bytes;
	private boolean explicit = true;

	// used by Error type
	private String errorMessage;
	private Throwable throwable;

	public AbstractAnswer(Type type, Tree expression) {
		this.type = type;
		this.expression = expression;
	}

	public AbstractAnswer(Type type) {
		this(type, null);
	}

	public AbstractAnswer(String errorMessage) {
		this(Type.Error, null);
		this.errorMessage = errorMessage;
	}

	public AbstractAnswer(Throwable throwable) {
		this(Type.Error, null);
		this.errorMessage = throwable.getMessage();
		this.throwable = throwable;
	}

	public AbstractAnswer(Tree expression) {
		this(Type.SExpression, expression);
	}

	public AbstractAnswer(byte[] bytes) {
		this(Type.Bytes);
		this.bytes = bytes;
	}

	@Override
	public byte[] toByteArray() {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try {
			switch (this.type) {
			case OK:
				baos.write(Toolbox.toUTF8Bytes(":ok"));
				break;
			case Bytes:
				baos.write(this.bytes);
				break;
			case SExpression:
				baos.write(SExpressionWriter.writeSExpression(this.expression));
				break;
			case Quit:
				baos.write(Toolbox.toUTF8Bytes(":ok"));
				break;
			case Event:
				baos.write(SExpressionWriter.writeSExpression(new InnerNode(
						new LeafNode(":event"), this.expression)));
				break;
			case Error:
			default:
				InnerNode errorExpression = new InnerNode(
						new LeafNode(":error"));
				if (this.throwable != null)
					errorExpression.add(Tree.fromObject(
							this.throwable.getClass(), explicit));
				errorExpression.add(this.errorMessage != null ? Tree
						.fromObject(this.errorMessage) : new LeafNode(
						"unexpected unknown error occurred"));
				if (this.throwable != null)
					errorExpression.add(Tree.fromObject(this.throwable,
							explicit));

				baos.write(SExpressionWriter.writeSExpression(errorExpression));
			}
			baos.write(Toolbox.toUTF8Bytes("\n"));
		} catch (IOException e) {
			LOGGER.error("error while writing to byte array", e);
		}
		return baos.toByteArray();
	}

	@Override
	public boolean endSession() {
		return this.type == Type.Quit;
	}

}
